﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reflection;
using System.Windows.Controls;
using D.Net.EmailInterfaces;
using Excel;
using ManagerClient.AuthentificationService;
using Microsoft.Practices.Prism.Commands;
using ValidationResult = System.ComponentModel.DataAnnotations.ValidationResult;

namespace ManagerClient.Messaging.Logic
{
    public class IncomingMailViewModel : ViewModelBase
    {
        public enum ProviderDescription
        {
            Gmail,
            MailRu,
        }

        private AuthentificationServiceClient client;

        private IEnumerable<Dish> dishes;
        private bool hasMenuItems;
        private IEnumerable<IEmail> mails;

        private string message;
        private string recipientMail;
        private IEmail selectedMail;
        private ProviderDescription selectedMailProvider;
        private string senderMail;
        private string senderName;
        private string subject;

        public IncomingMailViewModel(AuthentificationServiceClient client)
        {
            this.client = client;
            mails = new List<IEmail>();
            GetMailCommand = new DelegateCommand<object>(p =>
                {
                    IsBusy = true;
                    LoadMailsAsync(p).ObserveOnDispatcher().
                                      Finally(() => IsBusy = false).Subscribe(
                                          emails => { Mails = emails; }
                        );
                    ;
                });

            LoadMenuFromMail = new DelegateCommand<IEmail>(email =>
                {
                    dishes = GetDishesFromMessage(email);
                    IObservable<Unit> func = Observable.FromAsyncPattern<Dish[], int>(
                        client.BeginAddDishes, client.EndAddDishes)(dishes.ToArray(), 1);
                    func.
                        ObserveOnDispatcher().
                        Finally(() => { IsBusy = false; }
                        )
                        .Subscribe(x => { },
                                   e => { }
                        );
                });
            MailProviderNames = new List<ProviderDescription>
                {
                    ProviderDescription.Gmail,
                    ProviderDescription.MailRu,
                };
        }


        public DelegateCommand<IEmail> LoadMenuFromMail { get; set; }

        public ProviderDescription SelectedMailProvider
        {
            get { return selectedMailProvider; }
            set
            {
                selectedMailProvider = value;
                RaisePropertyChanged(() => SelectedMailProvider);
            }
        }

        public List<ProviderDescription> MailProviderNames { get; set; }

        public IEnumerable<IEmail> Mails
        {
            get { return mails; }
            private set
            {
                mails = value;
                RaisePropertyChanged(() => Mails);
            }
        }

        public IEmail SelectedMail
        {
            get { return selectedMail; }
            set
            {
                selectedMail = value;
                HasMenuItems = selectedMail.Attachments.Any(x => x.Text.Contains(".xls"));
                Subject = selectedMail.Subject;
                Message = selectedMail.TextBody;
                RaisePropertyChanged(() => SelectedMail);
            }
        }

        public DelegateCommand<object> GetMailCommand { get; private set; }

        [DisplayName("SenderName")]
        [Required]
        public string SenderName
        {
            get { return senderName; }
            set
            {
                senderName = value;
                RaisePropertyChanged(() => SenderName);
            }
        }

        [DisplayName("SenderMail")]
        [Required]
        public string SenderMail
        {
            get { return senderMail; }
            set
            {
                senderMail = value;
                RaisePropertyChanged(() => SenderMail);
            }
        }

        [DisplayName("RecipientMail")]
        [Required]
        public string RecipientMail
        {
            get { return recipientMail; }
            set
            {
                recipientMail = value;
                RaisePropertyChanged(() => RecipientMail);
            }
        }

        [Required]
        public string Subject
        {
            get { return subject; }
            set
            {
                subject = value;
                RaisePropertyChanged(() => Subject);
            }
        }

        [Required]
        public string Message
        {
            get { return message; }
            set
            {
                message = value;
                RaisePropertyChanged(() => Message);
            }
        }

        public bool HasMenuItems
        {
            get { return hasMenuItems; }
            set
            {
                hasMenuItems = value;
                RaisePropertyChanged(() => HasMenuItems);
            }
        }


        public string this[string property]
        {
            get
            {
                PropertyInfo propertyInfo = GetType().GetProperty(property);
                var results = new List<ValidationResult>();

                bool result = Validator.TryValidateProperty(
                    propertyInfo.GetValue(this, null),
                    new ValidationContext(this, null, null)
                        {
                            MemberName = property
                        }, results
                    );

                if (!result)
                {
                    ValidationResult validationResult = results.First();
                    return validationResult.ErrorMessage;
                }
                return string.Empty;
            }
        }

        public IObservable<IEnumerable<IEmail>> LoadMailsAsync(object param)
        {
            return Observable.Create<IEnumerable<IEmail>>(
                o => Observable.ToAsync<object, IEnumerable<IEmail>>(GetMails)(param).Subscribe(o)
                );
        }

        private IEnumerable<IEmail> GetMails(object passwordBox)
        {
            var p = (PasswordBox) passwordBox;
            var mp = new MailProvider();
            IEnumerable<IEmail> messages = mp.ReceiveMessagesFromProvider(new MailInfo
                {
                    Body = Message,
                    Credentials =
                        new UserCredentials
                            {
                                ClientlientType =
                                    SelectedMailProvider == ProviderDescription.Gmail
                                        ? SmtpClientType.Google
                                        : SmtpClientType.MailRu,
                                Login = SenderMail,
                                Password = p.Password
                            },
                    Subject = Subject,
                    To = RecipientMail
                });
            return messages;
        }

        private IEnumerable<Dish> GetDishesFromMessage(IEmail message)
        {
            var result = new List<Dish>();


            Stream stream = new MemoryStream(message.Attachments[0].Body);
            using (IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream))
            {
                DataRowCollection res = excelReader.AsDataSet().Tables[0].Rows;
                foreach (DataRow row in res)
                {
                    if (res.IndexOf(row) != 0)
                    {
                        object[] dishItem = row.ItemArray;

                        result.Add(new Dish
                            {
                                Name = dishItem[0].ToString(),
                                CalorificEffect = (double?) dishItem[1],
                                Structure = (string) dishItem[2],
                                Price = (double) dishItem[3],
                            }
                            )
                            ;
                    }
                }
            }
            return result;
        }
    }
}